package pl.edu.mimuw.irs.core.model.design;

import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import pl.edu.mimuw.irs.core.exception.InternalException;
import pl.edu.mimuw.irs.core.exception.InternalException.Cause;
import pl.edu.mimuw.irs.core.model.analysis.workflow.AnalysisWorkflow;
import pl.edu.mimuw.irs.core.model.design.processing.Resource;
import pl.edu.mimuw.irs.core.model.design.processing.Role;
import pl.edu.mimuw.irs.core.model.design.processing.RoleInstance;
import pl.edu.mimuw.irs.core.model.petrinet.INode;
import pl.edu.mimuw.irs.core.model.petrinet.Place;
import pl.edu.mimuw.irs.core.model.petrinet.workflow.Workflow;
import pl.edu.mimuw.irs.core.util.Converter;
import pl.edu.mimuw.irs.core.util.Position;


/**
 * @author Jakub Rauch
 * Created on: 2009-05-16
 */
public class IRSWorkflow extends Workflow implements Serializable {

	private static final long serialVersionUID = 2579015617442316214L;
	
	// *** Attributes
	
	private IRSPlace rootPlace;
	private Map<String, Role> rolesMap;
	private Set<Resource> resources;
	private Set<IRSRequirement> requirements;
	private Set<IRSActivity> activities;
	public IRSPlace getRootPlace() {
		return rootPlace;
	}
	public void setRootPlace(IRSPlace rootPlace) {
		this.rootPlace = rootPlace;
		this.addPlace(rootPlace);
	}
	/* This map should NOT be modified */
	public Map<String, Role> getRolesMap() {
		if (rolesMap == null) rolesMap = new LinkedHashMap<String, Role>();
		return rolesMap;
	}
	public Role getRole(String name) {
		return getRolesMap().get(name);
	}
	public void addRole(String name) {
		if (!getRolesMap().containsKey(name)) {
			getRolesMap().put(name, new Role(name));
		}
	}
	public void addRole(Role role) {
		if (!getRolesMap().containsKey(role.getName())) {
			getRolesMap().put(role.getName(), role);
		}
	}
	public void removeRole(String name) {
		if (getRolesMap().containsKey(name)) {
			getRolesMap().remove(name);
		}
	}
	public void removeRole(Role role) {
		if (getRolesMap().containsKey(role.getName())) {
			getRolesMap().remove(role.getName());
		}
	}
	public Set<Resource> getResources() {
		if (resources == null) resources = new LinkedHashSet<Resource>();
		return resources;
	}
	public void setResources(Set<Resource> resources) {
		this.resources = resources;
	}
	public void addResource(Resource resource) {
		// XXX : sprawdzic, czy wszystkie role sa obslugiwane
		getResources().add(resource);
	}
	public void addResource(Set<RoleInstance> roles) {
		getResources().add(new Resource(roles));
	}
	public void removeResource(Resource resource) {
		getResources().remove(resource);
	}
	public Set<IRSRequirement> getRequirements() {
		if (requirements == null) requirements = new LinkedHashSet<IRSRequirement>();
		return requirements;
	}
	public void setRequirements(Set<IRSRequirement> resources) {
		this.requirements = resources;
	}
	public Set<IRSActivity> getActivities() {
		if (activities == null) activities = new LinkedHashSet<IRSActivity>();
		return activities;
	}
	public void setActivities(Set<IRSActivity> activities) {
		this.activities = activities;
	}
	
	// *** Methods

	public void createSampleModel() {
		setRootPlace(new IRSPlace(getIdManager(), new Position(50, 50)));
		//getRootPlace().setFragmentation(new Fragmentation(Type.SEQUENCE));
		//getRootPlace().getChildren().get(1).setFragmentation(new Fragmentation(Type.ACTIVITY));
		createSampleContainers();
	}
	
	private void createSampleContainers() {
		// XXX Delete!
		addRole("A");
		addRole("B");
		addRole("C");
		addRole("D");
		addRole("E");
		
		RoleInstance roleA = new RoleInstance(getRole("A"), 1);
		RoleInstance roleB = new RoleInstance(getRole("B"), 1);
		RoleInstance roleC = new RoleInstance(getRole("C"), 1);
		RoleInstance roleD = new RoleInstance(getRole("D"), 1);
		RoleInstance roleE = new RoleInstance(getRole("E"), 1);
		
		Set<RoleInstance> setA = new LinkedHashSet<RoleInstance>();
		Set<RoleInstance> setAB = new LinkedHashSet<RoleInstance>();
		Set<RoleInstance> setABC = new LinkedHashSet<RoleInstance>();
		Set<RoleInstance> setC = new LinkedHashSet<RoleInstance>();
		Set<RoleInstance> setD = new LinkedHashSet<RoleInstance>();
		Set<RoleInstance> setE = new LinkedHashSet<RoleInstance>();
		
		setA.add(roleA);
		setAB.add(roleA);
		setAB.add(roleB);
		setABC.add(roleA);
		setABC.add(roleB);
		setABC.add(roleC);
		setC.add(roleC);
		setD.add(roleD);
		setE.add(roleE);
		
		Resource resourceA = new Resource(setA);
		Resource resourceAB = new Resource(setAB);
		Resource resourceABC = new Resource(setABC);
		Resource resourceC = new Resource(setC);
		Resource resourceD = new Resource(setD);
		Resource resourceE = new Resource(setE);
		
		addResource(resourceA);
		addResource(resourceAB);
		addResource(resourceABC);
		addResource(resourceC);
		addResource(resourceD);
		addResource(resourceE);
	}
	
	public void verifyWorkflow() {
		// TODO : verify workflow
	}
	
	public AnalysisWorkflow getAnalysisWorkflow() {
		return new AnalysisWorkflow(this);
	}
	
	@Override
	public Set<INode> getAllNodes() {
		Set<INode> nodes = new LinkedHashSet<INode>();
		for (INode node : getRootPlace().getExpandedNodes()) {
			nodes.add(node);
		}
		return nodes;// XXX : prawdopodobnie trzeba tu kopiować i wyrzucać zbędne łuki
	}
	/*
	@Override
	public Collection<Place> getPlaces() {
		Set<Place> nodes = new LinkedHashSet<Place>();
		for (INode node : getRootPlace().getExpandedNodes()) {
			if (node instanceof Place) {
				nodes.add((Place)node);
			}
		}
		return nodes;
	}

	@Override
	public Collection<Transition> getTransitions() {
		Set<Transition> nodes = new LinkedHashSet<Transition>();
		for (INode node : getRootPlace().getExpandedNodes()) {
			if (node instanceof Place) {
				nodes.add((Transition)node);
			}
		}
		return nodes;
	}
	*/
	
	protected void locateInOut() {
		boolean foundIn = false;
		boolean foundOut = false;
		setIn(null);
		setOut(null);
		
		for (Place place : getPlaces()) {
			if (place instanceof IRSPlace) {
				IRSPlace irsPlace = Converter.asIRSPlace(place);
				if (irsPlace.getIncomingArcs().size() == 0 &&
						irsPlace.isAtomic()) {
					if (foundIn) throw new InternalException(Cause.WORKFLOW_TOO_MANY_IN_PLACES);
					setIn(place);
					foundIn = true;
				}
			}
		}

		for (Place place : getPlaces()) {
			if (place instanceof IRSPlace) {
				IRSPlace irsPlace = Converter.asIRSPlace(place);
				if (irsPlace.getOutgoingArcs().size() == 0 &&
						irsPlace.isAtomic()) {
					if (foundOut) throw new InternalException(Cause.WORKFLOW_TOO_MANY_OUT_PLACES);
					setOut(place);
					foundOut = true;
				}
			}
		}
		
		if (!foundIn) throw new InternalException(Cause.WORKFLOW_NO_IN_PLACE);
		if (!foundOut) throw new InternalException(Cause.WORKFLOW_OUT_PLACE);
	}
}
